// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::PathBuf;
use eyre::{eyre, Context, ContextCompat};
use rerun::{external::{clap::{self, Parser}, log::{error, info}, re_log}, Vec3D};
use dora_node_api::{DoraNode, Event, EventStream};
use rerun_tools::{dora_data::DataKind, trajectory_utils::trajectory_from_str};

#[derive(Debug, Parser)]
#[clap(author, version, about)]
struct Args {
    /// The path to trajectory file
    path: PathBuf,
}

fn run(mut node: DoraNode, mut events: EventStream, trajectory: Vec<Vec3D>) -> eyre::Result<()> {
    let mut has_sent_trajectory = true;
    loop {
        let event = match events.recv() {
            Some(input) => input,
            None => break,
        };
        match event {
            Event::Stop(_) => {
                info!("dora stop");
                return Ok(());
            },
            Event::Reload { operator_id } => {
                return Err(eyre!("unhandled dora reload event: {:?}", operator_id));
            },
            Event::Input { id, metadata: _, data: _ } => {
                match id.as_str() {
                    "tick" => {
                        if has_sent_trajectory {
                            let bytes: &[u8] = unsafe {
                                std::slice::from_raw_parts(
                                    trajectory.as_ptr() as *const u8,
                                    trajectory.len() * size_of::<Vec3D>()
                                )
                            };
                            node.send_output_bytes(
                                String::from(DataKind::Trajectory.as_output_id()).into(),
                                Default::default(),
                                bytes.len(),
                                bytes,
                            )?;
                            has_sent_trajectory = false;
                        }
                    },
                    other => {
                        error!("unexpected input: {}", other);
                    }
                }
            },
            Event::InputClosed { id } => {
                info!("dora input {} closed", id);
                return Ok(());
            },
            Event::Error(msg) => {
                return Err(eyre!("received dora error event: {}", msg));
            },
            _ => {
                error!("unknown dora event: {:?}", event);
            }
        }
    }

    Ok(())
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
    re_log::setup_logging();
    let args = Args::try_parse()?;
    let trajectory = trajectory_from_str(&std::fs::read_to_string(&args.path)
        .context(format!("failed to read '{}'", args.path.display()))?)
        .context(format!("failed to parse trajectory file '{}'", args.path.display()))?;
    let (node, events) = DoraNode::init_from_env()?;
    run(node, events, trajectory)?;
    Ok(())
}